package rpc_netty.Client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.AttributeKey;
import jdk.jfr.events.SocketReadEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import rpc_1.Client.RpcClientProxy;
import rpc_1.interfaces.HelloService;
import rpc_1.object.HelloObject;
import rpc_1.object.RpcRequest;
import rpc_1.object.RpcResponse;
import rpc_netty.Encoder.CommonDecoder;
import rpc_netty.Encoder.CommonEncoder;
import rpc_netty.Handler.NettyClientHandler;
import rpc_netty.Serializer.JsonSerializer;
import rpc_netty.Serializer.KryoSerializer;
import rpc_netty.interfaces.RpcClient;

import javax.imageio.plugins.jpeg.JPEGImageReadParam;

public class NettyClient implements RpcClient {
    private static final Logger logger= LoggerFactory.getLogger(NettyClient.class);

    private String host;
    private int port;
    private static final Bootstrap bootstrap;

    static {
        EventLoopGroup worker=new NioEventLoopGroup();
        bootstrap=new Bootstrap();
        bootstrap.group(worker)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE,true)//心跳机制
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new CommonDecoder());
                        //pipeline.addLast(new CommonEncoder(new JsonSerializer()));
                        pipeline.addLast(new CommonEncoder(1));
                        pipeline.addLast(new NettyClientHandler());
                    }
                });
    }

    public NettyClient(String host,int port){
        this.host=host;
        this.port=port;
    }

    @Override
    public Object sendRequest(RpcRequest rpcRequest) {
        try {
            ChannelFuture future=bootstrap.connect(host,port).sync();
            logger.info("connect to the server {}-{}",host,port);
            Channel channel = future.channel();
            if (channel!=null){
                ChannelFuture futureFinal= channel.writeAndFlush(rpcRequest);
                futureFinal.addListener(future1 -> {
                    if (future1.isSuccess()){
                        logger.info(String.format("client send the message:%s",rpcRequest.toString()));
                    }else {
                        logger.error("error when sending the message");
                        logger.error(future1.cause().getMessage());
                    }
                });
                /*
                只有当这个channel被关闭的时候，sync方法才会返回
                而在NettyClientHandler中，在客户端收到信息（rpcResponse），并且往channel的attr中加入值后
                就会让这个channel关闭，从而这里的syn结束阻塞，并且通过attr拿到对应的rpcResponse

                NettyClientHandler中的代码：
                    AttributeKey<RpcResponse> key = AttributeKey.valueOf("rpcResponse");
                    ctx.channel().attr(key).set(msg);
                    ctx.channel().close();
                 */
                channel.closeFuture().sync();
                AttributeKey<RpcResponse> key= AttributeKey.valueOf("rpcResponse");
                RpcResponse rpcResponse = channel.attr(key).get();
                return rpcResponse;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            logger.error("error when sending the message",e);
        }
        return null;
    }

    public static void main(String[] args) throws InterruptedException {
        NettyClient client=new NettyClient("localhost",8825);
        RpcClientProxy_netty rpcClientProxy_netty = new RpcClientProxy_netty(client);
        HelloService proxy = rpcClientProxy_netty.getProxy(HelloService.class);
        HelloObject helloObject=new HelloObject(100,"guten!");
        String hello = proxy.hello(helloObject);
        System.out.println(hello);


    }
}
